using System;
using System.ServiceModel.Syndication;
using System.Xml;

namespace ServiceModel.Syndication.LEAP2A {
    /// <summary>
    /// An Atom link with additions for LEAP2A.
    /// </summary>
    public class LeapLink : SyndicationLink {
        /// <summary>
        /// A reference to the main feed for access to the namespaces, needed to deal with CURIEs.
        /// </summary>
        private readonly LeapFeed feed;

        private readonly XmlQualifiedName orderName;
        private readonly XmlQualifiedName dateName;

        /// <summary>
        /// Initializes a new instance of the <see cref="LeapLink"/> class.
        /// </summary>
        /// <param name="feed">The atom feed.</param>
        public LeapLink(LeapFeed feed) {
            this.feed = feed;
            this.orderName = new XmlQualifiedName("display_order", feed.LeapPredicatesNamespace);
            this.dateName = new XmlQualifiedName("when_added", feed.LeapPredicatesNamespace);
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="LeapLink"/> class.
        /// </summary>
        /// <param name="feed">The atom feed.</param>
        /// <param name="uri">The URI of the linked item.</param>
        public LeapLink(LeapFeed feed, Uri uri)
            : this(feed) {
            this.Uri = uri;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="LeapLink"/> class.
        /// </summary>
        /// <param name="feed">The atom feed.</param>
        /// <param name="relationshipType">Type of the relationship.</param>
        /// <param name="uri">The URI of the linked item.</param>
        public LeapLink(LeapFeed feed, LeapPredicates relationshipType, Uri uri)
            : this(feed) {
            this.LeapRelationshipType = relationshipType;
            this.Uri = uri;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="LeapLink"/> class.
        /// </summary>
        /// <param name="feed">The atom feed.</param>
        /// <param name="relationshipType">Type of the relationship.</param>
        /// <param name="uri">The URI of the linked item.</param>
        /// <param name="order">The ordniate of the link.</param>
        public LeapLink(LeapFeed feed, LeapPredicates relationshipType, Uri uri, int order)
            : this(feed) {
            this.LeapRelationshipType = relationshipType;
            this.Uri = uri;
            this.Order = order;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="LeapLink"/> class.
        /// </summary>
        /// <param name="feed">The atom feed.</param>
        /// <param name="relationshipType">Type of the relationship.</param>
        /// <param name="uri">The URI of the item.</param>
        /// <param name="order">The ordniate of the link.</param>
        /// <param name="date">The date associated with the link.</param>
        public LeapLink(LeapFeed feed, LeapPredicates relationshipType, Uri uri, int order, DateTime date)
            : this(feed, relationshipType, uri, order) {
            this.Date = date;
        }

        /// <summary>
        /// Gets or sets the order of this link, this is optional and only used when order matters
        /// i.e. the order of pages in the Website or postings to a blog, 
        /// see http://wiki.cetis.ac.uk/2009-03/LEAP2A_literals#display_order.
        /// </summary>
        /// <value>The order.</value>
        public int? Order {
            get {
                if (this.AttributeExtensions.ContainsKey(orderName)) {
                    int order;
                    if (int.TryParse(this.AttributeExtensions[orderName], out order)) {
                        return order;
                    }
                }

                return null;
            }

            set {
                if (value.HasValue) {
                    this.AttributeExtensions[orderName] = value.Value.ToString();
                }
            }
        }

        /// <summary>
        /// Gets or sets the date associated with this link, used to record the posting date of
        /// items added to a blog or similar selection methods, 
        /// see http://wiki.cetis.ac.uk/2009-03/LEAP2A_literals#when_added.
        /// </summary>
        /// <value>The link date.</value>
        public DateTime? Date {
            get {
                if (this.AttributeExtensions.ContainsKey(dateName)) {
                    DateTime date;
                    if (DateTime.TryParse(this.AttributeExtensions[dateName], out date)) {
                        return date;
                    }
                }

                return null;
            }

            set {
                if (value.HasValue) {
                    this.AttributeExtensions[dateName] = value.Value.ToString("s");
                }
            }
        }

        /// <summary>
        /// Gets or sets the URI for the linked resource, CURIEs are accepted but will be converted to a full link.
        /// Comparisons should always use full links, CURIEs are only used when outputting the Atom feed.
        /// </summary>
        /// <value>The link URI.</value>
        /// <returns>A <see cref="T:System.Uri"/> instance that contains the URI for the linked resource.</returns>
        public new Uri Uri {
            get {
                return base.Uri != null
                           ? new Uri(this.feed.UriFromCurie(base.Uri.ToString()), UriKind.RelativeOrAbsolute)
                           : null;
            }

            set {
                base.Uri = new Uri(feed.UriToCurie(value.ToString()), UriKind.RelativeOrAbsolute);
            }
        }

        /// <summary>
        /// Gets or sets the relationship type between the linked resources.
        /// </summary>
        /// <value>The relationship type, standard LEAP2A links are available as statics in LeapPredicates.
        /// Also see LeapItem.Has for an easier way to create link pairs.</value>
        /// <returns>The relationship type.</returns>
        public new string RelationshipType {
            get { return feed.UriFromCurie(base.RelationshipType); }
            set { base.RelationshipType = feed.UriToCurie(value); }
        }

        /// <summary>
        /// Gets or sets the relationship type between the linked resources.
        /// </summary>
        /// <value>The relationship type, standard LEAP2A links are available as statics in LeapPredicates.
        /// Also see LeapItem.Has for an easier way to create link pairs.</value>
        /// <returns>The relationship type.</returns>
        public LeapPredicates LeapRelationshipType {
            get {
                return LeapPredicateValues.FromUrl(this.RelationshipType, feed);
            }

            set {
                this.RelationshipType = LeapPredicateValues.UrlFor(value, feed);
            }
        }
    }
}
